import os
import re
import socket
from datetime import datetime, date, time
from typing import Union

from common import constants


class Util:

    @staticmethod
    def get_time_gap(next_time: datetime) -> int:
        """
        获取当前时间距离指定时间的间隔
        :param next_time: 下一个时间
        :return: 间隔时间（秒）
        """
        current_time = datetime.now()
        time_difference = next_time - current_time
        return int(time_difference.total_seconds())

    @staticmethod
    def calculate_elapsed_time(start_time: datetime, end_time: datetime) -> float:
        """
        计算时差
        :param start_time: 开始时间
        :param end_time: 结束时间
        :return 时差（时）
        """
        elapsed_time_seconds = (end_time - start_time).total_seconds()
        elapsed_time_hours = round(elapsed_time_seconds / 3600, 3)
        return elapsed_time_hours

    @staticmethod
    def date_in_days(data: date, days: int) -> bool:
        """
        判断指定日期是否在当前日期的指定范围内
        :param data: 要判断的日期，无时间的格式
        :param days: 天数
        """
        today = datetime.today().date()
        date_diff = data - today
        if 0 <= date_diff.days < days:
            return True
        else:
            return False

    @staticmethod
    def in_time_interval(start_hour: time, end_hour: time) -> bool:
        """
        判断当前时间是否在指定的时间范围
        :param start_hour: 范围的开始时间
        :param end_hour: 范围的结束时间
        :return: True or False
        """
        now_time = datetime.now().time()
        return start_hour <= now_time < end_hour

    @staticmethod
    def time_format_match(time_s: str) -> bool:
        """
        判断时间字符是否符合"HH:MM(:SS)"的要求
        :param time_s: 时间字符
        """
        # 定义严格的正则表达式，只允许 HH:MM 格式
        # 使用正则表达式匹配时间字符串
        time_p = re.match(constants.TIMED_TIME_PATTERN, time_s)
        # 返回匹配结果
        if time_p:
            # 进一步检查小时是否在 00-23 之间
            house, minute = map(int, time_s.split(':'))
            return 0 <= house <= 23
        return False

    @staticmethod
    def get_local_ip() -> str:
        """
        获取本地机器的 IP 地址
        """
        s = None
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(("8.8.8.8", 80))
            ip = s.getsockname()[0]
        except Exception as e:
            ip = "0.0.0.0"
        finally:
            if s:
                s.close()
        return ip

    @staticmethod
    def find_files_with_suffix(folder_path: str, suffix: str) -> list[str]:
        """
        扫描文件夹下所有包含指定后缀的文件
        :param folder_path: 文件夹
        :param suffix: 文件后缀
        :return: 符合条件的文件完整路径列表
        """
        matching_files = []
        # 遍历指定文件夹下的所有文件和子文件夹
        for root, dirs, files in os.walk(folder_path):
            for file in files:
                # 检查文件是否以指定后缀结尾
                if file.endswith(suffix):
                    # 构建符合条件的文件的完整路径
                    file_path = os.path.join(root, file).replace('\\', '/')
                    matching_files.append(file_path)
        return matching_files

    @staticmethod
    def find_project_root() -> Union[str, None]:
        """
        查找根目录
        :return: 根目录的绝对路径
        """
        current_dir = os.path.abspath(os.path.dirname(__file__))
        while True:
            # 检查是否存在特定主程序，以判断是否为项目根目录
            if (
                    os.path.exists(os.path.join(current_dir, constants.MAIN_PROGRAM)) or
                    os.path.exists(os.path.join(current_dir, f"{constants.MAIN_PROGRAM}.py")) or
                    os.path.exists(os.path.join(current_dir, f"{constants.MAIN_PROGRAM}.exe"))
            ):
                return current_dir
            # 向上一级目录继续查找
            parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir))
            # 如果已经到达根目录，则退出循环
            if parent_dir == current_dir:
                break
            current_dir = parent_dir
        # 如果未找到特定文件或目录，则返回 None
        return None

    @staticmethod
    def unified_static_path(static_type: str) -> str:
        """
        获取静态文件统一的保存目录（相对于根目录）
        :param static_type: 静态文件的类型，截图、日志或者其它
        :return: 系统标准目录
        """
        root_directory = Util.find_project_root()
        if root_directory is None:
            root_directory = "./"
        project_path = f"{root_directory}/{static_type}"
        if not os.path.exists(project_path):
            os.makedirs(project_path)
        # 路径标准化，统一目录符号
        return os.path.normpath(project_path)

    @staticmethod
    def get_data_path() -> str:
        return Util.unified_static_path(constants.DATA_SAVE_DIRECTORY)

    @staticmethod
    def get_file_path(dir_path: str, file_name: str) -> str:
        file_path = f"{dir_path}/{file_name}"
        return os.path.normpath(file_path)
